package main

import (
	"fmt"
	"math"
	"math/big"
	"strconv"
	"strings"
	"sync"
)

// ValueType represents the type of a Scheme value
type ValueType int

const (
	TypeInteger ValueType = iota
	TypeRational
	TypeReal
	TypeComplex
	TypeBoolean
	TypeSymbol
	TypeString
	TypeChar
	TypeList
	TypePair // For dotted pairs
	TypeVector // For vectors
	TypeFunction
	TypeBuiltin
	TypeNil
	TypeContinuation
TypeEOF
)

// NumberType represents the exact/inexact nature of numbers
type NumberType int

const (
	Exact NumberType = iota
	Inexact
)

// Value represents any Scheme value
type Value struct {
	Type ValueType
	// Numerical values
	Integer  *big.Int   // For exact integers
	Rational *big.Rat   // For exact rationals
	Real     float64    // For inexact reals
	Complex  complex128 // For complex numbers
	Exact    bool       // Whether the number is exact
	// Other values
	Boolean   bool
	Symbol    string
	StringVal string
	Char      rune
	List      []*Value
	Vector    []*Value   // For vectors
	Function  *Function
	Builtin   BuiltinFunc
	Continuation *Value  // For continuation values
	// Pair-specific fields for dotted pairs
	Car *Value // First element of a pair
	Cdr *Value // Second element of a pair
}

// Function represents a user-defined function (lambda)
type Function struct {
	Parameters []string
	Body       *Value
	Closure    *Environment
}

// BuiltinFunc represents a built-in function
type BuiltinFunc func(args []*Value, env *Environment) (*Value, error)

// String returns a string representation of the value
func (v *Value) String() string {
	switch v.Type {
	case TypeInteger:
		return v.Integer.String()
	case TypeRational:
		// Don't display as fraction if denominator is 1
		if v.Rational.Denom().Cmp(big.NewInt(1)) == 0 {
			return v.Rational.Num().String()
		}
		return v.Rational.String()
	case TypeReal:
		// Format as integer if it's a whole number and exact
		if v.Exact && v.Real == float64(int64(v.Real)) {
			return strconv.FormatInt(int64(v.Real), 10)
		}
		return strconv.FormatFloat(v.Real, 'g', -1, 64)
	case TypeComplex:
		// Format complex numbers according to R4RS
		realPart := real(v.Complex)
		imagPart := imag(v.Complex)

		// If imaginary part is 0, display as real number
		if imagPart == 0 {
			if v.Exact && realPart == float64(int64(realPart)) {
				return strconv.FormatInt(int64(realPart), 10)
			}
			return strconv.FormatFloat(realPart, 'g', -1, 64)
		}

		// If real part is 0, display as pure imaginary
		if realPart == 0 {
			if imagPart == 1 {
				return "+i"
			} else if imagPart == -1 {
				return "-i"
			} else {
				return fmt.Sprintf("%gi", imagPart)
			}
		}

		// Display full complex number: a+bi or a-bi
		realStr := strconv.FormatFloat(realPart, 'g', -1, 64)
		var imagStr string
		if imagPart > 0 {
			if imagPart == 1 {
				imagStr = "+1i"
			} else {
				imagStr = fmt.Sprintf("+%gi", imagPart)
			}
		} else {
			if imagPart == -1 {
				imagStr = "-1i"
			} else {
				imagStr = fmt.Sprintf("%gi", imagPart) // negative sign already included
			}
		}
		return realStr + imagStr
	case TypeBoolean:
		if v.Boolean {
			return "#t"
		}
		return "#f"
	case TypeSymbol:
		return v.Symbol
	case TypeString:
		return fmt.Sprintf("\"%s\"", v.StringVal)
	case TypeChar:
		// Format character according to R4RS
		switch v.Char {
		case ' ':
			return "#\\space"
		case '\t':
			return "#\\tab"
		case '\n':
			return "#\\newline"
		case '\r':
			return "#\\return"
		default:
			return fmt.Sprintf("#\\%c", v.Char)
		}
	case TypeList:
		if len(v.List) == 0 {
			return "()"
		}
		var elements []string
		for _, elem := range v.List {
			elements = append(elements, elem.String())
		}
		return fmt.Sprintf("(%s)", strings.Join(elements, " "))
	case TypeVector:
		var elements []string
		for _, elem := range v.Vector {
			elements = append(elements, elem.String())
		}
		return fmt.Sprintf("#(%s)", strings.Join(elements, " "))
	case TypePair:
		// Check if this is part of a proper list
		if v.isProperList() {
			// Display as proper list: (a b c)
			elements := v.collectListElements()
			var elemStrings []string
			for _, elem := range elements {
				elemStrings = append(elemStrings, elem.String())
			}
			return fmt.Sprintf("(%s)", strings.Join(elemStrings, " "))
		} else {
			// Display as dotted pair: (a . b)
			return fmt.Sprintf("(%s . %s)", v.Car.String(), v.Cdr.String())
		}
	case TypeFunction:
		return fmt.Sprintf("#<function %v>", v.Function.Parameters)
	case TypeBuiltin:
		return "#<builtin>"
	case TypeNil:
		return "()"
	default:
		return "#<unknown>"
	}
}

// Helper functions to create values

// NewInteger creates an exact integer value
func NewInteger(n int64) *Value {
	return &Value{
		Type:    TypeInteger,
		Integer: big.NewInt(n),
		Exact:   true,
	}
}

// NewIntegerBig creates an exact integer value from big.Int
func NewIntegerBig(n *big.Int) *Value {
	return &Value{
		Type:    TypeInteger,
		Integer: new(big.Int).Set(n),
		Exact:   true,
	}
}

// NewRational creates an exact rational value
func NewRational(num, den int64) *Value {
	r := big.NewRat(num, den)
	// If it's actually an integer, return as integer
	if r.Denom().Cmp(big.NewInt(1)) == 0 {
		return NewIntegerBig(r.Num())
	}
	return &Value{
		Type:     TypeRational,
		Rational: r,
		Exact:    true,
	}
}

// NewRationalBig creates an exact rational value from big.Rat
func NewRationalBig(r *big.Rat) *Value {
	// If it's actually an integer, return as integer
	if r.Denom().Cmp(big.NewInt(1)) == 0 {
		return NewIntegerBig(r.Num())
	}
	return &Value{
		Type:     TypeRational,
		Rational: new(big.Rat).Set(r),
		Exact:    true,
	}
}

// NewReal creates a real number value (exact or inexact)
func NewReal(n float64, exact bool) *Value {
	return &Value{
		Type:  TypeReal,
		Real:  n,
		Exact: exact,
	}
}

// NewInexactReal creates an inexact real number value
func NewInexactReal(n float64) *Value {
	return NewReal(n, false)
}

// NewExactReal creates an exact real number value
func NewExactReal(n float64) *Value {
	return NewReal(n, true)
}

// NewComplex creates a complex number value (exact or inexact)
func NewComplex(c complex128, exact bool) *Value {
	return &Value{
		Type:    TypeComplex,
		Complex: c,
		Exact:   exact,
	}
}

// NewInexactComplex creates an inexact complex number value
func NewInexactComplex(c complex128) *Value {
	return NewComplex(c, false)
}

// NewExactComplex creates an exact complex number value
func NewExactComplex(c complex128) *Value {
	return NewComplex(c, true)
}

// NewRectangular creates a complex number from real and imaginary parts
func NewRectangular(realPart, imagPart float64, exact bool) *Value {
	return NewComplex(complex(realPart, imagPart), exact)
}

// NewPolar creates a complex number from magnitude and angle
func NewPolar(magnitude, angle float64, exact bool) *Value {
	// Convert polar to rectangular: z = r * e^(i*θ) = r * (cos(θ) + i*sin(θ))
	realPart := magnitude * math.Cos(angle)
	imagPart := magnitude * math.Sin(angle)
	return NewComplex(complex(realPart, imagPart), exact)
}

// NewNumber creates a number value (backward compatibility)
// Defaults to inexact real for floating point, exact integer for whole numbers
func NewNumber(n float64) *Value {
	if n == float64(int64(n)) && math.Abs(n) <= math.MaxInt64 {
		return NewInteger(int64(n))
	}
	return NewInexactReal(n)
}

func NewBoolean(b bool) *Value {
	return &Value{Type: TypeBoolean, Boolean: b}
}

var (
	// symbolTable maintains a global map of symbol names to Value objects
	// for symbol interning
	symbolTable = make(map[string]*Value)
	// symbolTableMutex protects concurrent access to the symbol table
	symbolTableMutex sync.RWMutex
)

// Modify the NewSymbol function to use symbol interning
func NewSymbol(s string) *Value {
	symbolTableMutex.RLock()
	if symbol, exists := symbolTable[s]; exists {
		symbolTableMutex.RUnlock()
		return symbol
	}
	symbolTableMutex.RUnlock()

	// Create a new symbol if it doesn't exist
	symbol := &Value{Type: TypeSymbol, Symbol: s}

	symbolTableMutex.Lock()
	// Double-check to handle race conditions
	if existingSymbol, exists := symbolTable[s]; exists {
		symbolTableMutex.Unlock()
		return existingSymbol
	}
	symbolTable[s] = symbol
	symbolTableMutex.Unlock()

	return symbol
}

func NewString(s string) *Value {
	return &Value{Type: TypeString, StringVal: s}
}

func NewChar(c rune) *Value {
	return &Value{Type: TypeChar, Char: c}
}

func NewList(elements []*Value) *Value {
	return &Value{Type: TypeList, List: elements}
}

func NewPair(car, cdr *Value) *Value {
	return &Value{Type: TypePair, Car: car, Cdr: cdr}
}

func NewFunction(params []string, body *Value, closure *Environment) *Value {
	return &Value{
		Type: TypeFunction,
		Function: &Function{
			Parameters: params,
			Body:       body,
			Closure:    closure,
		},
	}
}

func NewBuiltin(fn BuiltinFunc) *Value {
	return &Value{Type: TypeBuiltin, Builtin: fn}
}

func NewNil() *Value {
	return &Value{Type: TypeNil}
}

// NewVoid creates a void value (for procedures that don't return a useful value)
func NewVoid() *Value {
	return NewNil()
}

// NewVector creates a new vector value
func NewVector(elements []*Value) *Value {
	// Create a copy of the elements slice to avoid unintended modifications
	vec := make([]*Value, len(elements))
	copy(vec, elements)
	return &Value{Type: TypeVector, Vector: vec}
}

// IsTruthy returns whether a value is considered true in Scheme
func (v *Value) IsTruthy() bool {
	if v == nil {
		return false
	}
	if v.Type == TypeBoolean {
		return v.Boolean
	}
	// In Scheme, everything except #f is truthy
	return true
}

// IsEqual checks if two values are equal
func (v *Value) IsEqual(other *Value) bool {
	if v == nil || other == nil {
		return v == other
	}

	// Special handling for empty list comparison
	// According to R4RS, all empty lists should be considered equal
	isVEmptyList := (v.Type == TypeNil) || (v.Type == TypeList && len(v.List) == 0)
	isOtherEmptyList := (other.Type == TypeNil) || (other.Type == TypeList && len(other.List) == 0)
	if isVEmptyList && isOtherEmptyList {
		return true
	}

	// Handle comparison between pairs and lists
	// A proper list built with pairs should equal a list with the same elements
	if (v.Type == TypePair && other.Type == TypeList) || (v.Type == TypeList && other.Type == TypePair) {
		return v.equalsPairList(other)
	}

	if v.Type != other.Type {
		// Allow comparison between different number types
		if v.IsNumber() && other.IsNumber() {
			return v.NumericEqual(other)
		}
		return false
	}

	switch v.Type {
	case TypeInteger:
		return v.Integer.Cmp(other.Integer) == 0
	case TypeRational:
		return v.Rational.Cmp(other.Rational) == 0
	case TypeReal:
		return v.Real == other.Real && v.Exact == other.Exact
	case TypeComplex:
		return v.Complex == other.Complex && v.Exact == other.Exact
	case TypeBoolean:
		return v.Boolean == other.Boolean
	case TypeSymbol:
		return v.Symbol == other.Symbol
	case TypeString:
		return v.StringVal == other.StringVal
	case TypeChar:
		return v.Char == other.Char
	case TypeNil:
		return true
	case TypeList:
		if len(v.List) != len(other.List) {
			return false
		}
		for i, elem := range v.List {
			if !elem.IsEqual(other.List[i]) {
				return false
			}
		}
		return true
	case TypeVector:
		if len(v.Vector) != len(other.Vector) {
			return false
		}
		for i, elem := range v.Vector {
			if !elem.IsEqual(other.Vector[i]) {
				return false
			}
		}
		return true
	case TypePair:
		return v.Car.IsEqual(other.Car) && v.Cdr.IsEqual(other.Cdr)
	default:
		return false
	}
}

// IsNumber checks if the value is a number
func (v *Value) IsNumber() bool {
	if v == nil {
		return false
	}
	return v.Type == TypeInteger || v.Type == TypeRational || v.Type == TypeReal || v.Type == TypeComplex
}

// NumericEqual compares two numbers for equality across types
func (v *Value) NumericEqual(other *Value) bool {
	if v == nil || other == nil {
		return false
	}
	if !v.IsNumber() || !other.IsNumber() {
		return false
	}

	// If either is complex, convert both to complex for comparison
	if v.Type == TypeComplex || other.Type == TypeComplex {
		vComplex := v.ToComplex()
		otherComplex := other.ToComplex()
		return vComplex == otherComplex
	}

	// Convert both to rationals for exact comparison
	vRat := v.ToRational()
	otherRat := other.ToRational()

	if vRat == nil || otherRat == nil {
		// Fall back to float comparison for inexact numbers
		return v.ToFloat() == other.ToFloat()
	}

	return vRat.Cmp(otherRat) == 0
}

// ToRational converts a number to rational representation
func (v *Value) ToRational() *big.Rat {
	switch v.Type {
	case TypeInteger:
		return new(big.Rat).SetInt(v.Integer)
	case TypeRational:
		return new(big.Rat).Set(v.Rational)
	case TypeReal:
		if v.Exact {
			return new(big.Rat).SetFloat64(v.Real)
		}
		// Inexact reals can't be converted to exact rationals
		return nil
	case TypeComplex:
		// Complex numbers can only be converted to rationals if imaginary part is 0
		if imag(v.Complex) == 0 {
			if v.Exact {
				return new(big.Rat).SetFloat64(real(v.Complex))
			}
		}
		return nil
	default:
		return nil
	}
}

// ToFloat converts a number to float64 representation
func (v *Value) ToFloat() float64 {
	switch v.Type {
	case TypeInteger:
		f, _ := new(big.Float).SetInt(v.Integer).Float64()
		return f
	case TypeRational:
		f, _ := new(big.Float).SetRat(v.Rational).Float64()
		return f
	case TypeReal:
		return v.Real
	case TypeComplex:
		// For complex numbers, return the real part
		return real(v.Complex)
	default:
		return 0
	}
}

// ToComplex converts a number to complex128 representation
func (v *Value) ToComplex() complex128 {
	switch v.Type {
	case TypeInteger:
		f, _ := new(big.Float).SetInt(v.Integer).Float64()
		return complex(f, 0)
	case TypeRational:
		f, _ := new(big.Float).SetRat(v.Rational).Float64()
		return complex(f, 0)
	case TypeReal:
		return complex(v.Real, 0)
	case TypeComplex:
		return v.Complex
	default:
		return complex(0, 0)
	}
}

// ToInteger converts a number to integer if possible
func (v *Value) ToInteger() *big.Int {
	switch v.Type {
	case TypeInteger:
		return new(big.Int).Set(v.Integer)
	case TypeRational:
		if v.Rational.Denom().Cmp(big.NewInt(1)) == 0 {
			return new(big.Int).Set(v.Rational.Num())
		}
		return nil
	case TypeReal:
		if v.Real == float64(int64(v.Real)) {
			return big.NewInt(int64(v.Real))
		}
		return nil
	default:
		return nil
	}
}

// isProperList checks if a pair forms a proper list (ends with nil or proper list)
func (v *Value) isProperList() bool {
	if v.Type != TypePair {
		return false
	}
	current := v
	for current.Type == TypePair {
		current = current.Cdr
	}
	// Proper list ends with nil, empty list, or any list (which extends the proper list)
	return current.Type == TypeNil || current.Type == TypeList
}

// collectListElements collects all elements from a proper list built with pairs
func (v *Value) collectListElements() []*Value {
	var elements []*Value
	current := v
	for current.Type == TypePair {
		elements = append(elements, current.Car)
		current = current.Cdr
	}
	// If the cdr is a non-empty list, append its elements
	if current.Type == TypeList && len(current.List) > 0 {
		elements = append(elements, current.List...)
	}
	return elements
}

// equalsPairList compares a pair structure with a list for equality
func (v *Value) equalsPairList(other *Value) bool {
	var pairValue, listValue *Value
	if v.Type == TypePair {
		pairValue = v
		listValue = other
	} else {
		pairValue = other
		listValue = v
	}

	// Only compare if one is a proper list
	if !pairValue.isProperList() {
		return false
	}

	// Get elements from both
	pairElements := pairValue.collectListElements()
	listElements := listValue.List

	// Compare element by element
	if len(pairElements) != len(listElements) {
		return false
	}

	for i, elem := range pairElements {
		if !elem.IsEqual(listElements[i]) {
			return false
		}
	}
	return true
}
